package cn.edu.sjtu.se.dslab.rhapsody.service;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Resource;

import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import cn.edu.sjtu.se.dslab.component.aadl.AADLModel;
import cn.edu.sjtu.se.dslab.component.aadl.AADLParser;
import cn.edu.sjtu.se.dslab.component.data.AvionicsEquipment;
import cn.edu.sjtu.se.dslab.component.data.DomainEntity;
import cn.edu.sjtu.se.dslab.component.design.DesignPatternComponent;
import cn.edu.sjtu.se.dslab.component.design.DesignPatternConst;
import cn.edu.sjtu.se.dslab.component.design.DesignPatternType;
import cn.edu.sjtu.se.dslab.component.dp.parse.DesignPatternInstanceBean;
import cn.edu.sjtu.se.dslab.component.dp.parse.Property;
import cn.edu.sjtu.se.dslab.component.method.InterfaceMethod;
import cn.edu.sjtu.se.dslab.component.method.MethodComponent;
import cn.edu.sjtu.se.dslab.project.BusZone;
import cn.edu.sjtu.se.dslab.project.DisplayZone;
import cn.edu.sjtu.se.dslab.project.EntityZone;
import cn.edu.sjtu.se.dslab.project.Project;
import cn.edu.sjtu.se.dslab.rhapsody.entity.Agent;
import cn.edu.sjtu.se.dslab.rhapsody.entity.BusAgent;
import cn.edu.sjtu.se.dslab.rhapsody.entity.ControlAgent;
import cn.edu.sjtu.se.dslab.rhapsody.entity.DisplayAgent;
import cn.edu.sjtu.se.dslab.rhapsody.entity.Event;
import cn.edu.sjtu.se.dslab.rhapsody.parse.UmlModel;
import cn.edu.sjtu.se.dslab.rhapsody.parse.XmiBean;
import cn.edu.sjtu.se.dslab.rhapsody.parse.XmiConst;
import cn.edu.sjtu.se.dslab.rhapsody.parse.XmiDocument;
import cn.edu.sjtu.se.dslab.rhapsody.parse.XmiSerializer;
import cn.edu.sjtu.se.dslab.rhapsody.parse.XmiUnit;
import cn.edu.sjtu.se.dslab.service.IAvionicsEquipmentService;
import cn.edu.sjtu.se.dslab.service.IBusZoneService;
import cn.edu.sjtu.se.dslab.service.IDesignPatternComponentService;
import cn.edu.sjtu.se.dslab.service.IDomainEntityService;
import cn.edu.sjtu.se.dslab.service.IEntityZoneService;
import cn.edu.sjtu.se.dslab.service.IInterfaceMethodService;
import cn.edu.sjtu.se.dslab.service.IMethodComponentService;
import cn.edu.sjtu.se.dslab.service.IProjectService;
import cn.edu.sjtu.se.dslab.util.FileUtil;
import cn.edu.sjtu.se.dslab.util.MD5;
import cn.edu.sjtu.se.dslab.util.Tuple;
import cn.edu.sjtu.se.dslab.util.TwoTuple;



@Service("rhapsodyFileService")
public class RhapsodyFileService {
	private static final Logger logger = Logger.getLogger(RhapsodyFileService.class);
	private static final String ACTOR_FILE_NAME = "actor.xmi";
	
	private static final String UPLOAD_RHAPSODY_FILE = "rhapsody_upload.xmi";
	
	@Resource(name="projectService")
	private IProjectService projectService;
	
	@Resource(name="aadlParser")
	private AADLParser aadlParser;
	
	@Resource(name="avionicsEquipmentService")
	private IAvionicsEquipmentService avionicsEquipmentService;
	
	@Resource(name="entityZoneService")
	private IEntityZoneService entityZoneService;
	
	@Resource(name="domainEntityService")
	private IDomainEntityService domainEntityService;
	
	@Resource(name="designPatternComponentService")
	private IDesignPatternComponentService designPatternComponentService;
	
	@Resource(name="interfaceMethodService")
	private IInterfaceMethodService interfaceMethodService;
	
	@Resource(name="methodComponentService")
	private IMethodComponentService methodComponentService;
	
	
	@Resource(name="busZoneService")
	private IBusZoneService busZoneService;
	
	public String generateActorFile(long projectId){
		XmiBean xmiBean = new XmiBean();
		UmlModel model = new UmlModel();
		model.setName("ImportedActorPkg");
		model.setId(generateXmiId(model.getName()));
		xmiBean.getUmlModels().add(model);
		logger.info(projectService);
		Project project = projectService.getProjectById(projectId);
		String fileRelativePath = project.getFolderPath() + ACTOR_FILE_NAME;
		
		AADLModel aadlModel = project.getModel();
		StringBuilder aadlPath = new StringBuilder(FileUtil.getRootPath());
		aadlPath.append("generate/").append(aadlModel.getXmlPath());
		File f = new File(aadlPath.toString());
		
		for (AvionicsEquipment ae : aadlParser.parseAADLToSysmlActor(f)){
			logger.info(ae.getName());
			XmiUnit unit = new XmiUnit();
			unit.setLabel(XmiConst.L_PACKAGED_ELEMENT);
			unit.setXmiId(generateXmiId(ae.getUri()));
			unit.setName(ae.getName());
			unit.setXmiType("uml:Actor");
			model.getChildren().add(unit);
		}
		xmiBean.save(fileRelativePath);
		return FileUtil.getRootPath() + fileRelativePath;
	}
	
	public void uploadRhapsodyXmiFile(long projectId, MultipartFile rhapsody_file) throws IOException{
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No such project id:" + projectId);
		String rhapSodyDir = project.getFolderPath() + "rhapsody/";
		FileUtil.createDirectorys(rhapSodyDir);
		logger.info("Rhapsody file path:" + UPLOAD_RHAPSODY_FILE);
		project.setRhapsodyFile(rhapSodyDir + UPLOAD_RHAPSODY_FILE);
		projectService.updateProject(project);
		StringBuilder filePath = new StringBuilder();
		filePath.append(FileUtil.getRootPath()).append(rhapSodyDir).append(UPLOAD_RHAPSODY_FILE);
		FileUtils.copyInputStreamToFile(rhapsody_file.getInputStream(), new File( filePath.toString()));
	}
	
	private String generateXmiId(String data){
		String prefix = "GUID+";
		int length = 32;
		char c = '-';
		int firstIndex = 8;
		int secondIndex = 13;
		int thirdIndex = 18;
		int fourthIndex = 23;
		
		MD5 md5 = new MD5();
		String uri = md5.getMD5ofStr(data + System.currentTimeMillis());
		StringBuilder hash = new StringBuilder(uri.substring(0, length).toLowerCase());
		hash.insert(firstIndex, c).insert(secondIndex, c).insert(thirdIndex, c).insert(fourthIndex, c);
		hash.insert(0, prefix);
		//logger.info(hash);
		return hash.toString();
	}
	
	public String generateActorAndDevice(long projectId){
		XmiBean xmiBean = new XmiBean();
		UmlModel model = new UmlModel();
		model.setName("ImportedActorPkg");
		model.setId(generateXmiId(model.getName()));
		xmiBean.getUmlModels().add(model);
		logger.info(projectService);
		Project project = projectService.getProjectById(projectId);
		String fileRelativePath = project.getFolderPath() + ACTOR_FILE_NAME;
		
		AADLModel aadlModel = project.getModel();
		StringBuilder aadlPath = new StringBuilder(FileUtil.getRootPath());
		aadlPath.append("generate/").append(aadlModel.getXmlPath());
		File f = new File(aadlPath.toString());
		
		for (AvionicsEquipment ae : aadlParser.parseAADLToSysmlActor(f)){
			logger.info(ae.getName());
			XmiUnit unit = new XmiUnit();
			unit.setLabel(XmiConst.L_PACKAGED_ELEMENT);
			unit.setXmiId(generateXmiId(ae.getUri()));
			unit.setName(ae.getName());
			unit.setXmiType("uml:Actor");
			model.getChildren().add(unit);
		}
		
		xmiBean.save(fileRelativePath);
		return FileUtil.getRootPath() + fileRelativePath;
	}
	
	public boolean saveXmiParseResult(long projectId){
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		String rhapsodyFile = project.getRhapsodyFile();
		if (null == rhapsodyFile || "".equals(rhapsodyFile)) return false;
		XmiDocument doc = new XmiDocument(rhapsodyFile);
		XmiSerializer serializer = new XmiSerializer();
		serializer.writeDisplayZones(doc, project.getFolderPath());
		serializer.writeEntityZones(doc, project.getFolderPath());
		serializer.writeBusAgents(doc, project.getFolderPath());
		serializer.writeControlResponse(doc, project.getFolderPath());
		serializer.writeBusZones(doc, project.getFolderPath());
		return true;
	}
	
	public List<DisplayZone> readXmiDisplayZones(long projectId){
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		String rhapsodyFile = project.getRhapsodyFile();
		if (null == rhapsodyFile || "".equals(rhapsodyFile)) return null;
		XmiSerializer serializer = new XmiSerializer();
		List<DisplayZone> displayZones = serializer.readDisplayZones(project.getFolderPath());
//		for (DisplayZone displayZone : displayZones){
//			logger.info("DisplayZone name="  + displayZone.getName() 
//					+ " size=" + displayZone.getSize() 
//					+ " entityUri=" + displayZone.getEntityUri());
//		}
		return displayZones;
	}
	
	/**
	 * 返回显示数据区和领域实体的映射二元组
	 * @param projectId
	 * @return
	 */
	public List<TwoTuple<DisplayZone, DomainEntity>> getDisplayZoneAndDomainEntityTuple(long projectId){
		List<DisplayZone>  displayZones = readXmiDisplayZones(projectId);
		List<TwoTuple<DisplayZone, DomainEntity>> result = new ArrayList<TwoTuple<DisplayZone, DomainEntity>>();
		if (null != displayZones){
			for (DisplayZone ez: displayZones){
				DomainEntity entity = domainEntityService.getDomainEntityByUri(ez.getEntityUri());
				result.add(Tuple.tuple(ez, entity));
			}
		}
		return result;
	}
	
	public List<EntityZone> readXmiEntityZones(long projectId){
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		String rhapsodyFile = project.getRhapsodyFile();
		if (null == rhapsodyFile || "".equals(rhapsodyFile)) return null;
		XmiSerializer serializer = new XmiSerializer();
		List<EntityZone> entityZones = serializer.readEntityZones(project.getFolderPath());		
		return entityZones;
	}
	
	/**
	 * 返回实体数据区和领域实体的映射二元组
	 * @param projectId
	 * @return
	 */
	public List<TwoTuple<EntityZone, DomainEntity>> getEntityZoneAndDomainEntityTuple(long projectId){
		List<EntityZone>  entityZones = readXmiEntityZones(projectId);
		List<TwoTuple<EntityZone, DomainEntity>> result = new ArrayList<TwoTuple<EntityZone, DomainEntity>>();
		if (null != entityZones){
			for (EntityZone ez: entityZones){
				DomainEntity entity = domainEntityService.getDomainEntityByUri(ez.getEntityUri());
				result.add(Tuple.tuple(ez, entity));
			}
		}
		return result;
	}
	
	
	public List<BusAgent> readXmiBusAgents(long projectId){
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		String rhapsodyFile = project.getRhapsodyFile();
		if (null == rhapsodyFile || "".equals(rhapsodyFile)) return null;
		XmiSerializer serializer = new XmiSerializer();
		List<BusAgent> busAgents = serializer.readBusAgent(project.getFolderPath());
		return busAgents;
	}
	
	public List<BusAgent> readDesignPatternBusAgents(long projectId){
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		//改成从设计模式文件文件中读取总线代理
	
		List<BusAgent> busAgents = new ArrayList<BusAgent>();
		DesignPatternComponent busDpc = designPatternComponentService
				.getDesignPatternCommponentByType(projectId,
						DesignPatternType.BUS);
		DesignPatternInstanceBean busDpiBean = new DesignPatternInstanceBean(
				DesignPatternType.BUS, busDpc.getXmlPath());
		List<Map<String, Property>> instances =  busDpiBean.getInstances();
		if (instances != null){
			for (Map<String, Property> instance : instances){
				BusAgent busAgent = new BusAgent();
				AvionicsEquipment equipment = avionicsEquipmentService.getAvionicsEquipmentByUri(
						instance.get(DesignPatternConst.BUS_EQUIPMENT).getValue());
				if (null != equipment){					
					busAgent.setName(equipment.getName());
					busAgent.setOuterSystem(equipment.getName());
				}
				
				String readInitMethod = instance.get(DesignPatternConst.BUS_READ_INIT_METHOD).getValue();
				if (readInitMethod != null && !"".equals(readInitMethod) && readInitMethod.contains("=")){
					String[] link = readInitMethod.split("=");
					InterfaceMethod method = interfaceMethodService
						.getInterfaceMethodById(Long.parseLong(link[0]));
					Event event = new Event();
					event.setName(method.getName());
					busAgent.setReadInitEvent(event);
				}
				
				String writeInitMethod = instance.get(DesignPatternConst.BUS_WRITE_INIT_METHOD).getValue();
				if (writeInitMethod != null && !"".equals(writeInitMethod) && writeInitMethod.contains("=")){
					String[] link = writeInitMethod.split("=");
					InterfaceMethod method = interfaceMethodService
						.getInterfaceMethodById(Long.parseLong(link[0]));
					Event event = new Event();
					event.setName(method.getName());
					busAgent.setWriteInitEvent(event);
				}
				
				String readProcessingMethod = instance.get(DesignPatternConst.BUS_READ_PROCESSING_METHOD).getValue();
				if (readProcessingMethod != null && !"".equals(readProcessingMethod) && readProcessingMethod.contains("=")){
					String[] link = readProcessingMethod.split("=");
					InterfaceMethod method = interfaceMethodService
						.getInterfaceMethodById(Long.parseLong(link[0]));
					Event event = new Event();
					event.setName(method.getName());
					busAgent.setReadProcessingEvent(event);
				}
				
				String writeProcessingMethod = instance.get(DesignPatternConst.BUS_WRITE_PROCESSING_METHOD).getValue();
				if (writeProcessingMethod != null && !"".equals(writeProcessingMethod) && writeProcessingMethod.contains("=")){
					String[] link = writeProcessingMethod.split("=");
					InterfaceMethod method = interfaceMethodService
						.getInterfaceMethodById(Long.parseLong(link[0]));
					Event event = new Event();
					event.setName(method.getName());
					busAgent.setWriteProcessingEvent(event);
				}
				
				String busZoneId = instance.get(DesignPatternConst.BUS_ZONE).getValue();
				BusZone busZone = busZoneService.getBusZoneById(Long.parseLong(busZoneId));
				
				String entityZoneIds = busZone.getEntityZones();
				if (entityZoneIds != null && !entityZoneIds.equals("")){
					String[] ezs = entityZoneIds.split("\\|");
					for (String id : ezs) {
						if (id.equals("") || id.equals("|"))
							continue;
						EntityZone entityZone = entityZoneService
								.getEntityZoneById(Long.parseLong(id));
						if (entityZone == null)
							throw new NullPointerException("No such entityZone!");
						DomainEntity entity = domainEntityService.getDomainEntityByUri(entityZone.getEntityUri());
						if (entity == null) continue;
						
						StringBuilder sb = new StringBuilder();
						//总线数据区名称为总线数据区类型小写加上_bz
						sb.append(busZone.getName().toLowerCase()).append("_bz.").append(entity.getName()).append("_p = ")
							.append(entity.getName().toLowerCase()).append(";");
						busAgent.getEntityZone().add(sb.toString());
					}
				}
				busAgents.add(busAgent);
			}
		}
		return busAgents;
	}
	
	public List<Event> readXmiControlResponses(long projectId){
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		String rhapsodyFile = project.getRhapsodyFile();
		if (null == rhapsodyFile || "".equals(rhapsodyFile)) return null;
		XmiSerializer serializer = new XmiSerializer();
		List<Event> events = serializer.readControlResponse(project.getFolderPath());
		return events;
	}
	
	
	/**
	 * 获得所有的代理
	 * @param projectId
	 * @return
	 */
	public List<Agent> getAllAgents(long projectId){
		List<Agent> agents = new ArrayList<Agent>();
		Agent displayAgent = new DisplayAgent();
		Agent controlAgent = new ControlAgent();
		agents.add(displayAgent);
		agents.add(controlAgent);
		//List<BusAgent> busAgents = readXmiBusAgents(projectId);
		List<BusAgent> busAgents = readDesignPatternBusAgents(projectId);
		if (busAgents!= null){
			for (BusAgent busAgent : busAgents){
				agents.add(busAgent);
			}
		}
		return agents;
	}

	public List<BusZone> readXmiBusZones(long projectId) {
		Project project = projectService.getProjectById(projectId);
		if (null == project) throw new NullPointerException("No Such Project exist projectId = [" + projectId + "]");
		String rhapsodyFile = project.getRhapsodyFile();
		if (null == rhapsodyFile || "".equals(rhapsodyFile)) return null;
		XmiSerializer serializer = new XmiSerializer();
		List<BusZone> busZones = serializer.readBusZones(project.getFolderPath());
		return busZones;
	}
	
}
